// my_predictor.h - Daan Raman
// Meta predictor using gShare and Two-level adaptive branch predictor/
// We keep track of the percentage of good predictions for each branch predictor -
// the predictor with the highest overall hitrate can then give the call for prediction.

#include <iostream>
#include <string>
#include <cstdlib>
#include <stdio.h>
#include <stdlib.h>
#include <sstream>
#include <cstdlib>

using namespace std;

class my_update : public branch_update {
public:
	unsigned int address;
	unsigned int index;
};

class my_predictor : public branch_predictor {
public:
		
#define HISTORY_LENGTH	14
#define TWO_LEVEL_INDEX_BITS 11
#define GSHARE_INDEX_BITS 28
	
#define DIFFERENCE_THRESHOLD 5000
#define BITSTRING(length) ((1 << (length)) - 1)
	
	my_update u;
	branch_info bi;
	
	//TWO LEVEL
	unsigned int history;
	unsigned int two_level_counters[1 << 2*HISTORY_LENGTH];
	unsigned int two_level_cIndex;
	std::string s;
	std::stringstream out;
	//Global hashing index
	int gIndex;
	
	//GSHARE
	unsigned int gshare_counters[1<<GSHARE_INDEX_BITS];
	unsigned int gshare_cIndex;
	
	//META
	bool gshare_decision;
	bool two_level_decision;
	
	double gshare_hitRate;
	double two_level_hitRate;
	
	my_predictor (void) : history(0) { 
		gshare_decision = false;
		two_level_decision = false;
		
		gshare_hitRate = 0;
		two_level_hitRate = 0;
	}
	
	branch_update *predict (branch_info & b) {
		bi = b;
		if (b.br_flags & BR_CONDITIONAL) {
			//Save jump address
			u.address = b.address;
			
			//TWO LEVEL
			//Get index for address part 
			two_level_cIndex = u.address & ((1<< (TWO_LEVEL_INDEX_BITS)) -1);
			
			gIndex = history | (two_level_cIndex << HISTORY_LENGTH);
			
			
			if(two_level_hitRate > gshare_hitRate){
				if(two_level_counters[gIndex] >= 2){
					two_level_decision = true;
					u.direction_prediction(true);
					
				}else{
					two_level_decision = false;
					u.direction_prediction(false);
				}
			}
			
			//gShare
			if(gshare_hitRate >= two_level_hitRate){
				u.index = (history << (GSHARE_INDEX_BITS - HISTORY_LENGTH))  ^ (b.address & ((1<<GSHARE_INDEX_BITS)-1));
				gshare_decision = gshare_counters[u.index] >> 1;
				u.direction_prediction(gshare_counters[u.index] >> 1);
			}
			
		} else {
			u.direction_prediction (true);
		}
		u.target_prediction (0);
		return &u;
	}
	
	void update (branch_update *u, bool taken, unsigned int target) {
		if (bi.br_flags & BR_CONDITIONAL) {
			
			if(std::abs((int)(gshare_hitRate + two_level_hitRate)) > DIFFERENCE_THRESHOLD){
				gshare_hitRate = 0;
				two_level_hitRate = 0;
				//printf("Reset counters \n");
			}
			
			//UPDATE CHOICE - TOURNAMENT
			if(taken){
				//Check scoring
				if(gshare_decision && !two_level_decision){
					gshare_hitRate++;
				}
				
				if(!gshare_decision && two_level_decision){
					two_level_hitRate++;
				}
				
			}else{
				//Check scoring
				if(!gshare_decision && two_level_decision){
					gshare_hitRate++;
				}
				
				if(gshare_decision && !two_level_decision){
					two_level_hitRate++;
				}
			}
			
			//TWO LEVEL
			//Get index for address part 
			two_level_cIndex = ((my_update*)u)->address & ((1<< (TWO_LEVEL_INDEX_BITS)) -1);
			
			gIndex = history | (two_level_cIndex << HISTORY_LENGTH);
			
			
			if (taken) {
				//Update knowledge
				if(two_level_counters[gIndex] < 3){
					two_level_counters[gIndex]++;
				}
			} else {
				if(two_level_counters[gIndex] >0){
					two_level_counters[gIndex]--;
				}
			}
			
			//gshare
			unsigned int *c = &gshare_counters[((my_update*)u)->index];
			if (taken) {
				if (*c < 3) (*c)++;
			} else {
				if (*c > 0) (*c)--;
			}
			
			
		}
		
		//Update history table with latest info on jump
		history <<= 1;
		history |= taken;
		//printf("\n\n*****\n\n");
		history &= (1 << HISTORY_LENGTH) -1;
		
		
	}
	
	void bin (int num) 
	{ 
		while (num != 0) 
		{ 
			printf ("%d",num % 2); 
			num = num /2; 
		} 
		
		printf("\n");
	}
};
